package com.hq.ims.home.service.file;


import com.hq.ims.data.entity.AppFile;
import com.hq.ims.data.entityenum.EnumFileType;
import com.hq.ims.data.service.BaseAppFileService;
import com.hq.ims.data.utils.LocalConstant;
import com.hq.ims.data.vo.AppFileMapstruct;
import com.hq.utils.exception.CommonException;
import com.hq.utils.util.*;
import com.hq.web.utils.HttpUtils;
import com.hq.web.utils.WebUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FilenameUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 本地文件服务
 * jbp
 */
@Slf4j
@Service
public class LocalFileServiceImpl implements IFileService {

    @Resource
    private BaseAppFileService baseAppFileService;
    @Value("${userAttribute.uploadFileSaveDir}")
    private String uploadFileSaveDir;
    @Value("${userAttribute.downFileDir}")
    private String downloadFileDir;


    @Override
    public AppFileMapstruct.FileVo appUploadFile(MultipartFile file) {
        try {
            String oriName = file.getOriginalFilename();
            File saveFile = this.getRandomFileName(uploadFileSaveDir, oriName);
            //真正的上传
            String fileExtName = FilenameUtils.getExtension(oriName);
            if (StringUtils.isEmpty(fileExtName)) {
                //因为fastDfs框架中限了扩展名
                throw CommonException.build("文件扩展名不能为空");
            }
            //写入AppFile
            AppFile appFile = new AppFile();
            appFile.setFileMd5(StringUtils.EMPTY);
            appFile.setFileSize(file.getSize());
            appFile.setFileSuffix(fileExtName);
            appFile.setFileType(EnumFileType.File);
            appFile.setFileName(oriName);
            appFile.setPid(StringUtils.FH_ZERO);
            appFile.setAppId(StringUtils.EMPTY);
            appFile.setCreateDateTime(LocalDateTime.now());
            appFile.setStorePath(saveFile.getName());
            appFile.setIsFinishUpload(false);
            appFile.insert();

            //不存在就创建文件
            if (!saveFile.exists()) {
                FileUtils.createFileDir(saveFile);
            }
            //保存文件
            file.transferTo(new File(saveFile.getAbsolutePath()));

            //上传动作
            boolean isCanPreview = LocalConstant.PREVIEW_SUFFIX_LIST.contains(appFile.getFileSuffix().toLowerCase());

            //构键返回对象
            AppFileMapstruct.FileVo fileVo = new AppFileMapstruct.FileVo();
            fileVo.setFileId(appFile.getAppFileId());
            fileVo.setFileName(appFile.getFileName());
            fileVo.setFileSize(appFile.getFileSize());
            fileVo.setFileSuffix(appFile.getFileSuffix());
            fileVo.setFileDownloadUrl(String.format(DOWN_URL_FORMAT_STR, appFile.getAppFileId()));
            fileVo.setFilePreviewUrl(isCanPreview ? String.format(PREVIEW_URL_FORM_STR, appFile.getAppFileId()) : fileVo.getFileDownloadUrl());
            return fileVo;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }



    @Override
    public void appFileDown(List<String> fileIdList) {
        ListUtils.isNotExistsEleTh(fileIdList, "下载文件不能为空");
        if (fileIdList.size() > 1) {
            //多文件下载
            this.appMultiFileDown(fileIdList);
        } else {
            //单文件下载
            String fileId = fileIdList.get(0);
            //文件是否是目录
            boolean isDir = false;
            AppFile appFile = baseAppFileService.getById(fileId);
            isDir = appFile.getFileType() == EnumFileType.Dir;
            if (isDir) {
                this.appMultiFileDown(fileIdList);
            } else {
                //单文件下载
                HttpServletResponse response = WebUtils.getResponse();
                HttpUtils.setDownloadHeaderByFileType(response, appFile.getFileName(), false, appFile.getFileSize());
                try {
                    FileInputStream fis = new FileInputStream(this.getDownLoadAbsolutePath(appFile));
                    FileUtils.downFile(fis, response.getOutputStream());
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    @Override
    public void appMultiFileDown(List<String> fileIdList) {
        String dirPath = StringUtils.EMPTY;
        String zipPath = StringUtils.EMPTY;
        try {
            List<AppFileMapstruct.AppFileTree> fileTreeList = new ArrayList<>();
            List<AppFile> list = baseAppFileService.lambdaQuery().in(AppFile::getAppFileId, fileIdList).list();
            fileTreeList.addAll(JsonUtils.convertListObject(list, AppFileMapstruct.AppFileTree.class));
            this.recursionFindAppFile(fileIdList, fileTreeList);
            ListUtils.isNotExistsEleTh(fileTreeList, "没有可用文件下载");

            //组装父子关系
            fileTreeList = ParentChildrenClassificationUtils.classificationToTree(
                    fileTreeList,
                    k -> k.getAppFileId(),
                    k -> k.getPid(),
                    (k, v) -> {
                        if (ListUtils.isExistsEle(v)) {
                            k.setChildrenList(v);
                        }
                    },
                    d -> d.setPid(StringUtils.FH_ZERO),
                    StringUtils.FH_ZERO
            );
            dirPath = downloadFileDir + "/" + System.currentTimeMillis();
            zipPath = downloadFileDir + "/" + System.currentTimeMillis() + ".zip";
            this.recursionWriteZip(fileTreeList, dirPath);
            File dirFile = new File(dirPath);
            File zipile = new File(zipPath);
            HttpServletResponse response = WebUtils.getResponse();
            ZipUtils.doCompress(dirFile, zipile);
            //强制下载
            //报错,每次启动需要注释的地方
            HttpUtils.setDownloadHeaderByFileType(response, "package.zip", false, zipile.length());
            FileInputStream fis = new FileInputStream(zipile);
            FileUtils.downFile(fis, response.getOutputStream());
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                FileUtils.deleteFileDir(new File(dirPath));
                Files.delete(Paths.get(zipPath));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    public void userFileDown(List<String> fileIdList) {

    }

    @Override
    public void userMultiFileDown(List<String> fileIdList) {

    }

    /**
     * 递归写入文件
     *
     * @param fileTreeList 文件树列表
     * @param downDir      下载的目录
     */
    private void recursionWriteZip(
            List<AppFileMapstruct.AppFileTree> fileTreeList,
            String downDir
    ) {
        if (ListUtils.isNotExistsEle(fileTreeList)) {
            return;
        }
        try {
            for (AppFileMapstruct.AppFileTree fileTree : fileTreeList) {
                String filePath = downDir + "/" + fileTree.getFileName();
                File file = new File(filePath);

                if (fileTree.getFileType() == EnumFileType.File) {
                    FileUtils.createFile(file);
                    FileInputStream readIs = new FileInputStream(this.getDownLoadAbsolutePath(fileTree));
                    FileUtils.writeFile(readIs, filePath);
                } else {
                    FileUtils.createDir(file);
                    this.recursionWriteZip(fileTree.getChildrenList(), filePath);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw CommonException.build("下载出错");
        }
    }


    /**
     * 递归查找出所有app文件的文件
     *
     * @param pidList  pid列表
     * @param saveTree 保存的树结构
     */
    private void recursionFindAppFile(List<String> pidList, List<AppFileMapstruct.AppFileTree> saveTree) {
        List<AppFile> list = baseAppFileService.lambdaQuery().in(AppFile::getPid, pidList).list();
        if (ListUtils.isNotExistsEle(list)) {
            return;
        }

        List<String> childrenFolderIdList = list.stream().filter(d -> d.getFileType() == EnumFileType.Dir).map(AppFile::getAppFileId).collect(Collectors.toList());
        if (ListUtils.isExistsEle(childrenFolderIdList)) {
            this.recursionFindAppFile(childrenFolderIdList, saveTree);
        }
        saveTree.addAll(JsonUtils.convertListObject(list, AppFileMapstruct.AppFileTree.class));
    }

    @Override
    public void appFilePreview(String fileId) {
        try {
//            IAppInfo appInfo = ValidCenterUtils.getCurAccessTokenAppInfo();
            AppFile appFile = baseAppFileService.getById(fileId);
            ObjectUtils.isNullThrow(appFile, "文件不存在");

            String fileName = appFile.getFileName();
            String suffix = appFile.getFileSuffix();

            //是否是预览文件
            boolean isCanPreFile = LocalConstant.PREVIEW_SUFFIX_LIST.contains(suffix.toLowerCase());
            HttpServletResponse response = WebUtils.getResponse();
            if (!isCanPreFile) {
                throw CommonException.build("该文件不支持预览");
            }
            HttpUtils.setDownloadHeaderByFileType(response, fileName, true, appFile.getFileSize());
            FileInputStream fis = new FileInputStream(this.getDownLoadAbsolutePath(appFile));
            FileUtils.downFile(fis, response.getOutputStream());

        } catch (Exception e) {
            log.info("预览失败fileId:{}", fileId);
            e.printStackTrace();
            return;
        }
    }

    @Override
    public void userFilePreview(String fileId) {

    }

    /**
     * 获取文件绝对路径
     */
    public String getDownLoadAbsolutePath(AppFile appFile) {
        return downloadFileDir + "/" + appFile.getStorePath();
    }
}
